package com.cl.util;

import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.stereotype.Component;

@Component
public class HibernateUtil {

	private static final SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");

	final private SimpleDateFormat dayStartFormatter = new SimpleDateFormat("yyyy-MM-dd 00:00:00");
	final private SimpleDateFormat dayEndFormatter = new SimpleDateFormat("yyyy-MM-dd 23:59:59");
	private static final String oralceDateFormat = "yyyy-mm-dd hh24:mi:ss";

	/**
	 * 
	 * @param filters
	 * @param entity
	 * @return
	 * @throws ParseException
	 * @throws NoSuchFieldException
	 * @throws SecurityException
	 * @throws IOException
	 * @throws JsonMappingException
	 * @throws JsonParseException
	 */
	public <T> List<Criterion> getRestrictions(List<ExtJSFilter> filters, Class<T> entity) throws ParseException,
			SecurityException, NoSuchFieldException, JsonParseException, JsonMappingException, IOException {
		List<Criterion> restrictions = new ArrayList<Criterion>();
		for (ExtJSFilter filter : filters) {
			//c4w 前台直接check,会导致filter value为空
			if(filter.getValue()==null)
				continue;
			String property = filter.getProperty();
			String type = getType(filter, entity);
			String value = getFormateStr(filter.getValue());
			if (type.equals("string")) {
				if ("eq".equals(filter.getOperator())) {
					restrictions.add(Restrictions.eq(property, filter.getValue())); // case insensitive
				} else {
					//c4w 由于缺少对为空的检索，将空格输入解析为is null
					if(filter.getValue().toString().trim().equals("")){
						restrictions.add(Restrictions.isNull(property)); // case insensitive						
					}else
						restrictions.add(Restrictions.ilike(property, "%" + filter.getValue() + "%")); // case insensitive
				}
			} else if (type.equals("boolean")) {
				restrictions.add(Restrictions.eq(property, (byte) (Boolean.parseBoolean(value) ? 1 : 0)));
			} else if (type.equals("numeric")) {
				restrictions.add(getComparison(filter.getOperator(), property, getNumericValue(filter, entity)));
			} else if (type.equals("date")) {
				if ("eq".equals(filter.getOperator())) {// 当天时间的过滤(当天与第二天之间时间段为当天)
					Date date = getDateValue(filter);// 当天的时间
					Date startDate = DateUtil.getDateByDayNum(date, 0, true);// 大于等于当天最小时间点
					restrictions.add(getComparison("ge", property, startDate));
					Date nextDate = DateUtil.getDateByDayNum(date, 1, true);// 小于第二天最小时间点
					restrictions.add(getComparison("lt", property, nextDate));
				} else if ("gt".equals(filter.getOperator())) {// 大于某天(大于等于第二天最小时间点) 
					Date nextDate = DateUtil.getDateByDayNum(getDateValue(filter), 1, true);// 第二天的时间
					restrictions.add(getComparison("ge", property, nextDate));
				} else if ("ge".equals(filter.getOperator())) {// 大于等于某天(大于等于当天最小时间点)
					Date nextDate = DateUtil.getDateByDayNum(getDateValue(filter), 0, true);// 第二天的时间
					restrictions.add(getComparison(filter.getOperator(), property, nextDate));
				} else if ("le".equals(filter.getOperator())) {// 小于等于某天(小于第二天最小时间点)
					Date nextDate = DateUtil.getDateByDayNum(getDateValue(filter), 1, true);// 第二天的时间
					restrictions.add(getComparison("lt", property, nextDate));
				} else if("lt".equals(filter.getOperator())) {// 小于某天(小于当天最小时间点)
					Date nextDate = DateUtil.getDateByDayNum(getDateValue(filter), 0, true);
					restrictions.add(getComparison(filter.getOperator(), property, nextDate));
				} else {
					restrictions.add(getComparison(filter.getOperator(), property, getDateValue(filter)));
				}
			} else if (type.equals("list")) {
				Object[] values = getObjectArray(filter, entity);
				if ((values != null) && (values.length > 0)) {
					if (values.length > 1) {
						restrictions.add(Restrictions.in(property, values));
					} else {
						restrictions.add(Restrictions.eq(property, values[0]));
					}
				}
			}
		}
		return restrictions;
	}

	/**
	 * 获取集合数据，numberic需要特别处理
	 * @param filter
	 * @param entity
	 * @param <T>
	 * @return
	 * @throws NoSuchFieldException
	 */
	@SuppressWarnings("rawtypes")
	public <T> Object[] getObjectArray(ExtJSFilter filter, Class<T> entity) throws NoSuchFieldException{
		List values = (List) filter.getValue();
		if ((values != null) && (values.size() > 0)) {
			Object[] objs = new Object[values.size()];
			for (int i = 0; i < values.size(); i++) {
				ExtJSFilter tempFilter = new ExtJSFilter();
				tempFilter.setProperty(filter.getProperty());
				tempFilter.setValue(values.get(i));
				String type = getType(tempFilter, entity);
				if ("numeric".equals(type)) {
					objs[i] = getNumericValue(tempFilter, entity);
				} else {
					objs[i] = values.get(i);
				}
			}
			return objs;
		}
		return null;
	}

	public List<Order> getOrders(List<ExtJSSort> sorts) {
		List<Order> orders = new ArrayList<Order>();
		for (ExtJSSort sort : sorts) {
			String propertyName = sort.getProperty();
			boolean ascending = "ASC".equals(sort.getDirection()) ? true : false;
			if (ascending) {
				orders.add(Order.asc(propertyName));
			} else {
				orders.add(Order.desc(propertyName));
			}
		}
		return orders;
	}

	/**
	 * 
	 * @param comparison
	 * @param field
	 * @param value
	 * @return
	 */
	private Criterion getComparison(String comparison, String field, Object value) {
		if (comparison.equals("eq")) {
			return Restrictions.eq(field, value);
		} else if (comparison.equals("lt")) {
			return Restrictions.lt(field, value);
		} else if (comparison.equals("gt")) {
			return Restrictions.gt(field, value);
		} else if (comparison.equals("ge")) {
			return Restrictions.ge(field, value);
		} else if (comparison.equals("le")) {
			return Restrictions.le(field, value);
		}
		return null;
	}

	private Date getDateValue(ExtJSFilter filter) throws ParseException {
		Object value = filter.getValue();
		String val = getFormateStr(value);
		return dateFormat.parse(val);
	}

	public String getJpqlDateValue(ExtJSFilter filter) throws ParseException {
		StringBuffer dateSQL = new StringBuffer();
		if (filter != null) {
			Date date = getDateValue(filter);
			if ("eq".equals(filter.getOperator())) {
				dateSQL.append(filter.getProperty());
				dateSQL.append(" < to_timestamp('");
				dateSQL.append(dayEndFormatter.format(date));
				dateSQL.append("', '" + oralceDateFormat + "') and ");
				dateSQL.append(filter.getProperty());
				dateSQL.append(" > to_timestamp('");
				dateSQL.append(dayStartFormatter.format(date));
				dateSQL.append("', '" + oralceDateFormat + "') ");
			} else if ("gt".equals(filter.getOperator())) {
				dateSQL.append(filter.getProperty());
				dateSQL.append(" > to_timestamp('");
				dateSQL.append(dayEndFormatter.format(date));
				dateSQL.append("', '" + oralceDateFormat + "') ");
			} else if ("lt".equals(filter.getOperator())) {
				dateSQL.append(filter.getProperty());
				dateSQL.append(" < to_timestamp('");
				dateSQL.append(dayStartFormatter.format(date));
				dateSQL.append("', '" + oralceDateFormat + "') ");
			} else if ("le".equals(filter.getOperator())) {
				dateSQL.append(filter.getProperty());
				dateSQL.append(" < to_timestamp('");
				// 小于第二天最小时间点
				Date nextDate = DateUtil.getDateByDayNum(date, 1, true);// 第二天的时间
				dateSQL.append(dayStartFormatter.format(nextDate));
				dateSQL.append("', '" + oralceDateFormat + "') ");
			} else if ("ge".equals(filter.getOperator())) {
				dateSQL.append(filter.getProperty());
				dateSQL.append(" >= to_timestamp('");
				dateSQL.append(dayStartFormatter.format(date));
				dateSQL.append("', '" + oralceDateFormat + "') ");
			}
		}
		return dateSQL.toString();
	}

	private <T> Object getNumericValue(ExtJSFilter filter, Class<T> entity) throws SecurityException,
			NoSuchFieldException {
		Field field = getClassField(entity, filter.getProperty());
		DefaultConversionService conversionService = new DefaultConversionService();
		return conversionService.convert(filter.getValue(), field.getType());
	}

	public <T> String getType(ExtJSFilter filter, Class<T> entity) throws SecurityException, NoSuchFieldException {
		String type = "string";
		Object value = filter.getValue();
		Field field = getClassField(entity, filter.getProperty());
		String fieldType = field.getType().toString();
		if ((value instanceof List) || "class java.util.List".equals(fieldType)) {
			type = "list";
		} else if ((value instanceof ArrayList) || "class java.util.ArrayList".equals(fieldType)) {
			type = "list";
		} else if ((value instanceof Integer) || "class java.lang.Integer".equals(fieldType)) {
			type = "numeric";
		} else if ((value instanceof Byte) || "class java.lang.Byte".equals(fieldType)) {
			type = "numeric";
		} else if ((value instanceof Long) || "class java.lang.Long".equals(fieldType)) {
			type = "numeric";
		} else if ((value instanceof Float) || "class java.lang.Float".equals(fieldType)) {
			type = "numeric";
		} else if ((value instanceof Double) || "class java.lang.Double".equals(fieldType)) {
			type = "numeric";
		} else if ((value instanceof Short) || "class java.lang.Short".equals(fieldType)) {
			type = "numeric";
		} else if ((value instanceof BigDecimal) || "class java.math.BigDecimal".equals(fieldType)) {
			type = "numeric";
		} else if ((value instanceof Date) || "class java.util.Date".equals(fieldType)) {
			type = "date";
		} else if ((value instanceof Boolean) || "class java.lang.Boolean".equals(fieldType)) {
			type = "boolean";
		}
		return type;
	}

	/**
	 * 
	 * @param entity
	 * @param property
	 * @return
	 * @throws NoSuchFieldException
	 * @throws SecurityException
	 */
	private <T> Field getClassField(Class<T> entity, String property) throws SecurityException, NoSuchFieldException {
		Field classField = entity.getDeclaredField(property);
		return classField;
	}

	/**
	 * 
	 * @param value
	 * @return
	 */
	private String getFormateStr(Object value) {
		String val = String.valueOf(value);
		return val;
	}
}
